package com.zj.eswrapper;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.zj.eswrapper.interfaces.impl.AbstractEsQueryWrapper;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author arthur_zhou
 * @date 2024/1/29 15:33
 */
public interface IMiddlewareQueryBuilder<QueryBuilder> {

    BigDecimal ZERO = new BigDecimal("0.0");

    /**
     * 创建条件拼接器
     *
     * @return QueryBuilder
     */
    AbstractEsQueryWrapper<String, ?, ?, ?, QueryBuilder, ?, ?> createWrapper();

    /**
     * 多字段匹配
     *
     * @param wrapper 条件拼接器
     * @param key     中间件es报文
     */
    void multiMatch(AbstractEsQueryWrapper<String, ?, ?, ?, QueryBuilder, ?, ?> wrapper, Key key);

    /**
     * 自定义地图图形的坐标筛选
     *
     * @param wrapper 条件拼接器
     * @param graph   中间件es报文
     */
    void graph(AbstractEsQueryWrapper<String, ?, ?, ?, QueryBuilder, ?, ?> wrapper, Graph graph);

    /**
     * 地理范围拼接
     *
     * @param wrapper 条件拼接器
     * @param key     查询字段
     * @param radius  圆半径
     * @param lon     经度
     * @param lat     维度
     */
    void geoDistance(AbstractEsQueryWrapper<String, ?, ?, ?, QueryBuilder, ?, ?> wrapper, String key, BigDecimal radius, BigDecimal lon, BigDecimal lat);

    /**
     * jsonStr转换成QueryBuilder
     *
     * @param jsonStr 中间件es的请求报文字符串
     * @return QueryBuilder
     */
    default QueryBuilder query(String jsonStr) {
        JSONObject json = JSONObject.parseObject(jsonStr);
        AbstractEsQueryWrapper<String, ?, ?, ?, QueryBuilder, ?, ?> wrapper = createWrapper();
        // 转换中间件filter里的条件
        JSONObject filterJson = json.getJSONObject("filter");
        Filter filter = filterJson.toJavaObject(Filter.class);
        wrapper.filter().bool(query(filter));
        // 转换中间件key里的条件
        JSONObject keyJson = json.getJSONObject("key");
        Key key = keyJson.toJavaObject(Key.class);
        multiMatch(wrapper, key);
        return wrapper.queryBuilder();
    }

    /**
     * jsonStr转换成QueryBuilder
     *
     * @param filter 中间件es的filter json
     * @return QueryBuilder
     */
    default AbstractEsQueryWrapper<?, ?, ?, ?, QueryBuilder, ?, ?> query(Filter filter) {
        AbstractEsQueryWrapper<String, ?, ?, ?, QueryBuilder, ?, ?> wrapper = createWrapper();
        query(wrapper, filter);
        return wrapper;
    }

    /**
     * jsonStr转换成QueryBuilder
     *
     * @param wrapper 条件拼接器
     * @param filter  中间件es的filter json
     */
    default void query(AbstractEsQueryWrapper<String, ?, ?, ?, QueryBuilder, ?, ?> wrapper, Filter filter) {
        if (Objects.isNull(filter)) {
            return;
        }
        // and
        List<Condition> and = filter.getAnd();
        if (CollectionUtil.isNotEmpty(and)) {
            makeFilter(wrapper, and);
        } else {
            // 注意：如果filter中and与or同级时，or可以一个都不匹配，如果需要至少匹配一个，需要把or放到and里面
            List<Condition> or = filter.getOr();
            wrapper.should();
            makeFilter(wrapper, or);
        }
        // not
        List<Condition> not = filter.getNot();
        wrapper.mustNot();
        makeFilter(wrapper, not);
    }

    /**
     * 拼接filter条件
     *
     * @param wrapper      条件拼接器
     * @param conditionArr 条件数组
     */
    default void makeFilter(AbstractEsQueryWrapper<String, ?, ?, ?, QueryBuilder, ?, ?> wrapper, List<Condition> conditionArr) {
        if (CollectionUtil.isEmpty(conditionArr)) {
            return;
        }
        for (Condition condition : conditionArr) {
            Map<String, Object> term = condition.getTerm();
            Map<String, Object> terms = condition.getTerms();
            Map<String, Object> range = condition.getRange();
            Map<String, Object> exists = condition.getExists();
            Map<String, Object> distanceRange = condition.getDistanceRange();
            Graph graph = condition.getGraph();
            Nested nested = condition.getNested();
            if (CollectionUtil.isNotEmpty(term)) {
                term(wrapper, term);
                continue;
            } else if (CollectionUtil.isNotEmpty(terms)) {
                terms(wrapper, terms);
                continue;
            } else if (CollectionUtil.isNotEmpty(range)) {
                range(wrapper, range);
                continue;
            } else if (CollectionUtil.isNotEmpty(exists)) {
                exists(wrapper, exists);
                continue;
            } else if (CollectionUtil.isNotEmpty(distanceRange)) {
                distanceRange(wrapper, distanceRange);
                continue;
            } else if (Objects.nonNull(graph)) {
                graph(wrapper, graph);
                continue;
            } else if (Objects.nonNull(nested)) {
                wrapper.nested(nested.getPath(), v -> query(v, nested));
                continue;
            }
            // 如果有子条件，在当前条件下嵌套子条件
            wrapper.bool(query(condition));
        }
    }

    /**
     * 地理范围拼接
     *
     * @param wrapper       条件拼接器
     * @param distanceRange 中间件es报文
     */
    default void distanceRange(AbstractEsQueryWrapper<String, ?, ?, ?, QueryBuilder, ?, ?> wrapper, Map<String, Object> distanceRange) {
        distanceRange.entrySet().stream().findFirst().filter(v -> StrUtil.isNotEmpty(v.getKey()) && Objects.nonNull(v.getValue())).ifPresent(v -> {
            String key = v.getKey();
            Object value = v.getValue();
            if (!(value instanceof JSONObject)) {
                return;
            }
            JSONObject jsonObject = (JSONObject) value;
            BigDecimal from = jsonObject.getBigDecimal("from");
            BigDecimal to = jsonObject.getBigDecimal("to");
            BigDecimal lon = jsonObject.getBigDecimal("lon");
            BigDecimal lat = jsonObject.getBigDecimal("lat");
            // 圆范围
            if (Objects.isNull(from) || ZERO.compareTo(from) >= 0) {
                geoDistance(wrapper, key, to, lon, lat);
                return;
            }
            // 圆环范围
            wrapper.bool(b -> {
                geoDistance(b, key, to, lon, lat);
                b.mustNot();
                geoDistance(b, key, from, lon, lat);
            });
        });

    }

    /**
     * 字段是否存在
     *
     * @param wrapper 条件拼接器
     * @param exists  中间件es报文
     */
    default void exists(AbstractEsQueryWrapper<String, ?, ?, ?, QueryBuilder, ?, ?> wrapper, Map<String, Object> exists) {
        exists.entrySet().stream().findFirst().filter(v -> StrUtil.isNotEmpty(v.getKey()) && Objects.nonNull(v.getValue())).ifPresent(v -> {
            Object value = v.getValue();
            wrapper.exists(String.valueOf(value));
        });
    }

    /**
     * @param wrapper 条件拼接器
     * @param range   中间件es报文
     */
    default void range(AbstractEsQueryWrapper<String, ?, ?, ?, QueryBuilder, ?, ?> wrapper, Map<String, Object> range) {
        range.entrySet().stream().findFirst().filter(v -> StrUtil.isNotEmpty(v.getKey()) && Objects.nonNull(v.getValue())).ifPresent(v -> {
            String key = v.getKey();
            Object value = v.getValue();
            if (value instanceof JSONObject) {
                JSONObject jsonObject = (JSONObject) value;
                Object gt = jsonObject.get("gt");
                Object gte = jsonObject.get("gte");
                Object lt = jsonObject.get("lt");
                Object lte = jsonObject.get("lte");
                wrapper.range(key, gt, gte, lt, lte, null, null, null, null, null);
            }
        });
    }

    /**
     * @param wrapper 条件拼接器
     * @param terms   中间件es报文
     */
    default void terms(AbstractEsQueryWrapper<String, ?, ?, ?, QueryBuilder, ?, ?> wrapper, Map<String, Object> terms) {
        terms.entrySet().stream().findFirst().filter(v -> StrUtil.isNotEmpty(v.getKey()) && Objects.nonNull(v.getValue())).ifPresent(v -> {
            String key = v.getKey();
            Object value = v.getValue();
            if (value instanceof Collection) {
                wrapper.terms(key, (Collection<?>) value);
            }
        });
    }

    /**
     * 等于
     *
     * @param wrapper 条件拼接器
     * @param term    中间件es报文
     */
    default void term(AbstractEsQueryWrapper<String, ?, ?, ?, QueryBuilder, ?, ?> wrapper, Map<String, Object> term) {
        term.entrySet().stream().findFirst().filter(v -> StrUtil.isNotEmpty(v.getKey()) && Objects.nonNull(v.getValue())).ifPresent(v -> {
            String key = v.getKey();
            Object value = v.getValue();
            wrapper.term(key, value);
        });
    }
}
